package static_data

import (
	"bytes"
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"

	"nggs/util"

	"nggs/tools/print"

	"github.com/tealeg/xlsx"
)

const (
	DefaultSchema = "t=int"

	NameSchemaKey        = "n"
	TypeSchemaKey        = "t"
	IndexSchemaKey       = "i"
	SliceSchemaKey       = "s"
	ObjectSchemaKey      = "o"  // todo
	ObjectSliceSchemaKey = "os" // todo
)

type Offset [2]int

func NewOffset(row int, column int) *Offset {
	return &Offset{row, column}
}

func (o Offset) Row() int {
	return o[0]
}

func (o *Offset) SetRow(r int) {
	o[0] = r
}

func (o Offset) Column() int {
	return o[1]
}

func (o *Offset) SetColumn(c int) {
	o[1] = c
}

func (o Offset) Check() bool {
	return o[0] != -1 && o[1] != -1
}

func (o Offset) String() string {
	return fmt.Sprintf("[%d,%d]", o[0], o[1])
}

type XlsxField struct {
	col          int
	Name         string
	Type         string
	realType     string
	Comment      string
	schemaString string
	schema       map[string]string
}

func NewXlsxField(col int, name string, comment string, schemaString string) (f *XlsxField) {
	f = &XlsxField{
		col:          col,
		Name:         name,
		Comment:      comment,
		schemaString: schemaString,
	}
	if f.schemaString == "" {
		f.schemaString = DefaultSchema
	}
	return
}

func (f *XlsxField) Format() (err error) {
	for _, c := range f.Name {
		if unicode.IsDigit(c) {
			// 第一个字符是数字的字段，前面加上n字母，避免生成失败
			f.Name = "n" + f.Name
		}
		break
	}

	if f.schemaString == "" {
		// 需要结构信息
		err = &ErrNeedSchema{}
		return
	}

	if f.schemaString[0] == '#' {
		err = &ErrNotField{}
		return
	}

	f.schema = ParseSchemaString(f.schemaString)

	t, ok := f.schema[TypeSchemaKey]
	if !ok {
		// 没有类型信息，则默认是整型
		t = "int"
	}

	// 修正字段类型
	f.Type = t
	f.realType = f.Type
	switch t {
	case "bool", "float64":
		f.realType = t
	}

	if n, ok := f.schema[NameSchemaKey]; ok {
		// 修正字段名
		f.Name = n
	}
	if i, ok := f.schema[IndexSchemaKey]; ok {
		// 判断字段索引类型
		_, ok := IndexName2Type(strings.ToLower(i))
		if !ok {
			err = fmt.Errorf("非法字段索引类型[%s]", i)
			return
		}
	}

	// 字段名称的第一个字母如果是大写，则转成小写
	f.Name = util.LowerFirst(f.Name)

	return
}

func (f XlsxField) String() string {
	ba, _ := json.Marshal(f)
	return string(ba)
}

type FieldType = int

const (
	FieldTypeBase FieldType = iota
	FieldTypeBaseSlice
	FieldTypeEnum
	FieldTypeEnumSlice
	FieldTypeObject      // todo
	FieldTypeObjectSlice // todo
)

type Field struct {
	ft         FieldType
	xlsxFields []*XlsxField
	Name       string
	Type       string
	realType   string
	Comment    string
	Column     int `json:"-"`
}

func NewField(ft FieldType, name string, tYpE string, realType string, comment string) (f *Field) {
	f = &Field{
		ft:       ft,
		Name:     name,
		Type:     tYpE,
		realType: realType,
		Comment:  comment,
	}
	return
}

func (f *Field) CheckAndFormat() (err error) {
	// 字段名称的第一个字母如果是大写，则转成小写
	f.Name = util.LowerFirst(f.Name)
	return
}

type Attribute struct {
	Field
	Value string `json:"-"`
	Row   int    `json:"-"`
}

func NewAttribute(name string, t string, value string, comment string, row int, col int) (a *Attribute, err error) {
	a = &Attribute{
		Field: Field{
			Name:    name,
			Type:    t,
			Comment: comment,
			Column:  col,
		},
		Value: value,
		Row:   row,
	}
	err = a.CheckAndFormat()
	return
}

type pair struct {
	first  string
	second interface{}
}

type Data []pair

func (d *Data) Append(k string, v interface{}) {
	*d = append(*d, pair{first: k, second: v})
}

func (d Data) MarshalJSON() (ba []byte, err error) {
	buf := bytes.NewBuffer(ba)
	buf.WriteString("{")
	l := len(d)
	for i, p := range d {
		switch p.second.(type) {
		case int, time.Duration:
			buf.WriteString(fmt.Sprintf(`"%s":%d`, p.first, p.second))
		case string:
			buf.WriteString(fmt.Sprintf(`"%s":"%s"`, p.first, p.second))
		case float64:
			buf.WriteString(fmt.Sprintf(`"%s":%v`, p.first, p.second))
		case bool:
			buf.WriteString(fmt.Sprintf(`"%s":%v`, p.first, p.second))
		default:
			bs, _ := json.Marshal(p.second)
			buf.WriteString(fmt.Sprintf(`"%s":%s`, p.first, string(bs)))
		}
		if i != l-1 {
			buf.WriteString(",")
		}
	}
	buf.WriteString("}")
	return buf.Bytes(), nil
}

type Sheet struct {
	Name          string
	Comment       string
	xlsxFields    []*XlsxField
	Fields        []*Field
	UniqueIndexes []*XlsxField `json:",omitempty"` // 唯一索引
	NormalIndexes []*XlsxField `json:",omitempty"` // 普通索引
	Attributes    []*Attribute
	Global        Data
	Datas         []Data

	SheetName            string  `json:"-"`
	TableNameOffset      *Offset `json:"-"`
	AttributeBeginOffset *Offset `json:"-"`
	DataBeginOffset      *Offset `json:"-"`
}

func NewSheet(sheetName string) *Sheet {
	return &Sheet{
		SheetName: sheetName,
	}
}

func (s *Sheet) AppendXlsxField(newField *XlsxField) (err error) {
	for _, f := range s.xlsxFields {
		if f.Name == newField.Name {
			return fmt.Errorf("字段名重复")
		}
	}
	s.xlsxFields = append(s.xlsxFields, newField)
	return
}

func (s *Sheet) AppendField(newField *Field) (err error) {
	for _, f := range s.Fields {
		if f.Name == newField.Name {
			return fmt.Errorf("分页[%s]字段名[%s]重复", s.Name, f.Name)
		}
	}
	s.Fields = append(s.Fields, newField)
	return
}

var gSlicePattern *regexp.Regexp

func parseValueToIntSlice(value string) (slice []int, err error) {
	ss := strings.Split(value, ",")
	for index, str := range ss {
		str = strings.TrimSpace(str)
		element, e := strconv.Atoi(str)
		if e != nil {
			err = fmt.Errorf("strconv.Atoi fail, index=%d, string=%s", index, str)
			return
		}
		slice = append(slice, element)
	}
	return
}

func parseValueToTimeDurationSlice(value string) (slice []time.Duration, err error) {
	ss := strings.Split(value, ",")
	for index, str := range ss {
		str = strings.TrimSpace(str)
		element, e := strconv.Atoi(str)
		if e != nil {
			err = fmt.Errorf("strconv.Atoi fail, index=%d, string=%s", index, str)
			return
		}
		slice = append(slice, time.Duration(element))
	}
	return
}

func parseValueToFloat64Slice(value string) (slice []float64, err error) {
	ss := strings.Split(value, ",")
	for index, str := range ss {
		str = strings.TrimSpace(str)
		element, e := strconv.ParseFloat(str, 64)
		if e != nil {
			err = fmt.Errorf("strconv.Atoi fail, index=%d, string=%s", index, str)
			return
		}
		slice = append(slice, element)
	}
	return
}

func parseValueToStringSlice(value string) (slice []string, err error) {
	ss := strings.Split(value, ",")
	for _, str := range ss {
		str = strings.TrimSpace(str)
		slice = append(slice, str)
	}
	return
}

func parseValueToBoolSlice(value string) (slice []bool, err error) {
	ss := strings.Split(value, ",")
	for index, str := range ss {
		b := true
		v := strings.ToLower(str)
		switch v {
		case "0", "f", "false", "否":
			b = false
		case "1", "t", "true", "是":
			b = true
		default:
			err = fmt.Errorf("parse bool string fail, index=%d, string=%s", index, str)
			return
		}
		slice = append(slice, b)
	}
	return
}

func (s *Sheet) Format(sd *StaticData, clientMode bool) (err error) {
	var sliceFields []*Field

	for _, xf := range s.xlsxFields {
		var f *Field
		if i, ok := xf.schema[IndexSchemaKey]; ok {
			switch i {
			case IndexNameUnique:
				s.UniqueIndexes = append(s.UniqueIndexes, xf)
			case IndexNameNormal:
				s.NormalIndexes = append(s.NormalIndexes, xf)
			}
		}

		if gSlicePattern.MatchString(xf.Name) {
			// 收集并归类切片字段
			i := strings.IndexAny(xf.Name, "0123456789")
			sliceName := xf.Name[:i]
			sliceComment := xf.Comment
			i = strings.IndexAny(xf.Comment, "0123456789")
			if i > 0 {
				sliceComment = xf.Comment[:i] + "的切片"
			}
			for _, sf := range sliceFields {
				if sf.Name == sliceName {
					f = sf
					break
				}
			}
			if f == nil {
				if _, ok := xf.schema[SliceSchemaKey]; ok {
					// 只有切片的第一个元素带切片标志，才会转换成切片
					// 切片的元素类型以第一个元素为准！！！
					if strings.HasPrefix(xf.Type, "E_") {
						e := GetEnumByName(sd.Enums, xf.Type)
						if e == nil {
							err = fmt.Errorf("分页[%s]字段[%s]对应的枚举[%s]不存在", s.Name, xf.Name, xf.Type)
							return
						}
						f = NewField(FieldTypeEnumSlice, sliceName, "[]"+xf.Type, "int", sliceComment)
					} else {
						f = NewField(FieldTypeBaseSlice, sliceName, "[]"+xf.Type, xf.realType, sliceComment)
					}
					f.xlsxFields = []*XlsxField{xf}
					sliceFields = append(sliceFields, f)
				} else {
					if strings.HasPrefix(xf.Type, "E_") {
						e := GetEnumByName(sd.Enums, xf.Type)
						if e == nil {
							err = fmt.Errorf("分页[%s]字段[%s]对应的枚举[%s]不存在", s.Name, xf.Name, xf.Type)
							return
						}
						f = NewField(FieldTypeEnum, xf.Name, xf.Type, "int", xf.Comment)
					} else {
						f = NewField(FieldTypeBase, xf.Name, xf.Type, xf.realType, xf.Comment)
					}
					f.xlsxFields = []*XlsxField{xf}
					err = s.AppendField(f)
					if err != nil {
						return
					}
				}
			} else {
				f.xlsxFields = append(f.xlsxFields, xf)
			}
		} else {
			if strings.HasPrefix(xf.Type, "E_") {
				e := GetEnumByName(sd.Enums, xf.Type)
				if e == nil {
					err = fmt.Errorf("分页[%s]字段[%s]对应的枚举[%s]不存在", s.Name, xf.Name, xf.Type)
					return
				}
				f = NewField(FieldTypeEnum, xf.Name, xf.Type, "int", xf.Comment)
			} else {
				f = NewField(FieldTypeBase, xf.Name, xf.Type, xf.realType, xf.Comment)
			}
			f.xlsxFields = []*XlsxField{xf}
			err = s.AppendField(f)
			if err != nil {
				return
			}
		}
	}

	for _, sf := range sliceFields {
		err = s.AppendField(sf)
		if err != nil {
			return
		}
	}

	//if len(s.Fields) == 0 {
	//	err = &ErrTableFieldEmpty{}
	//	return
	//}

loop:
	for _, attribute := range s.Attributes {
		var attributeName = attribute.Name
		if clientMode {
			attributeName = util.FormatFieldName(attributeName)
		}
		switch attribute.Type {
		case "int", "time.Duration":
			i, err := strconv.Atoi(attribute.Value)
			if err == nil {
				s.Global.Append(attributeName, i)
			} else {
				// 转成int失败，尝试转成float64
				f64, err := strconv.ParseFloat(attribute.Value, 64)
				if err == nil {
					s.Global.Append(attributeName, f64)
				} else {
					print.Errorf("分页[%s]属性[%s]的值必须是数字，%s", s.Name, attribute.Name, err)
					continue
				}
			}
		case "float64":
			f64, err := strconv.ParseFloat(attribute.Value, 64)
			if err == nil {
				s.Global.Append(attributeName, f64)
			} else {
				print.Errorf("分页[%s]属性[%s]的值必须是浮点数，%s", s.Name, attribute.Name, err)
				continue
			}
		case "bool":
			b := true
			v := strings.ToLower(attribute.Value)
			switch v {
			case "0", "f", "false", "否":
				b = false
			case "是":
				b = true
			}
			s.Global.Append(attributeName, b)
		default:
			if strings.HasPrefix(attribute.Type, "[]") {
				// 切片
				elementType := strings.TrimPrefix(attribute.Type, "[]")
				switch elementType {
				case "int", "time.Duration":
					var slice []int
					if attribute.Value != "" {
						err = json.Unmarshal([]byte(attribute.Value), &slice)
						if err != nil {
							slice, err = parseValueToIntSlice(attribute.Value)
							if err != nil {
								print.Errorf("分页[%s]属性[%s]的值必须是整型数组，%s", s.Name, attribute.Name, err)
								continue
							}
						}
					}
					s.Global.Append(attributeName, slice)
				case "string":
					var slice []string
					if attribute.Value != "" {
						err = json.Unmarshal([]byte(attribute.Value), &slice)
						if err != nil {
							slice, err = parseValueToStringSlice(attribute.Value)
							if err != nil {
								print.Errorf("分页[%s]属性[%s]的值必须是字符串数组，%s", s.Name, attribute.Name, err)
								continue
							}
						}
					}
					s.Global.Append(attributeName, slice)
				case "float64":
					var slice []float64
					if attribute.Value != "" {
						err = json.Unmarshal([]byte(attribute.Value), &slice)
						if err != nil {
							slice, err = parseValueToFloat64Slice(attribute.Value)
							if err != nil {
								print.Errorf("分页[%s]属性[%s]的值必须是浮点数数组，%s", s.Name, attribute.Name, err)
								continue
							}
						}
					}
					s.Global.Append(attributeName, slice)
				case "bool":
					var slice []bool
					if attribute.Value != "" {
						err = json.Unmarshal([]byte(attribute.Value), &slice)
						if err != nil {
							slice, err = parseValueToBoolSlice(attribute.Value)
							if err != nil {
								print.Errorf("分页[%s]属性[%s]的值必须是布尔数组，%s", s.Name, attribute.Name, err)
								continue
							}
						}
					}
					s.Global.Append(attributeName, slice)
				default:
					if strings.HasPrefix(elementType, "E_") {
						// todo 枚举切片
						e := GetEnumByName(sd.Enums, elementType)
						if e == nil {
							print.Errorf("分页[%s]属性[%s]对应的枚举[%s]不存在", s.Name, attribute.Name, elementType)
							continue
						}
						var slice []int
						if attribute.Value != "" {
							svs := strings.Split(attribute.Value, ",")
							for _, sv := range svs {
								v := e.GetValue(sv)
								if v == nil {
									print.Errorf("分页[%s]属性[%s]对应的枚举[%s]的值[%s]不存在", s.Name, attribute.Name, elementType, sv)
									continue loop
								}
								slice = append(slice, v.Value)
							}
						}
						s.Global.Append(attributeName, slice)
					} else {
						s.Global.Append(attributeName, attribute.Value)
					}
				}
			} else if strings.HasPrefix(attribute.Type, "E_") {
				// 枚举
				e := GetEnumByName(sd.Enums, attribute.Type)
				if e == nil {
					print.Errorf("分页[%s]属性[%s]对应的枚举[%s]不存在", s.Name, attribute.Name, attribute.Type)
					continue loop
				}
				v := e.GetValue(attribute.Value)
				if v == nil {
					print.Errorf("分页[%s]属性[%s]对应的枚举[%s]的值[%s]不存在", s.Name, attribute.Name, e.Name, attribute.Value)
					continue loop
				}
				s.Global.Append(attributeName, v.Value)
			} else {
				s.Global.Append(attributeName, attribute.Value)
			}
		}
	}

	return
}

func (s *Sheet) ReadData(sheet *xlsx.Sheet, sd *StaticData, clientMode bool) (err error) {
	// 读取数据
rowLoop:
	for row := s.DataBeginOffset.Row() + 4; row < sheet.MaxRow; row++ {
		ignore, _ := CanIgnoreSheetRow(sheet, row)
		if ignore {
			continue
		}
		data := Data{}
	filedLoop:
		for _, f := range s.Fields {
			fieldName := f.Name
			if clientMode {
				fieldName = util.FormatFieldName(fieldName)
			}
			switch f.ft {
			case FieldTypeBase:
				col := f.xlsxFields[0].col
				cell := sheet.Cell(row, col)
				if cell == nil {
					continue
				}
				// 去除左右空白字符
				value := strings.TrimSpace(cell.Value)
				if strings.ContainsAny(value, "\n") {
					// 将换行替换成空格，避免json序列化失败
					value = strings.ReplaceAll(value, "\n", " ")
				}
				if value == "" {
					continue
				}
				switch f.realType {
				case "int", "time.Duration":
					i, err := strconv.Atoi(value)
					if err == nil {
						if i != 0 {
							data.Append(fieldName, i)
						}
					} else {
						// 转成int失败，尝试转成float64
						f64, err := strconv.ParseFloat(value, 64)
						if err == nil {
							if f64 != 0 {
								data.Append(fieldName, f64)
							}
						} else {
							print.Errorf("分页[%s]第[%d]行第[%d]列必须是数字，%s", s.Name, row+1, col+1, err)
							continue rowLoop
						}
					}
				case "float64":
					f64, err := strconv.ParseFloat(value, 64)
					if err == nil {
						if f64 != 0 {
							data.Append(fieldName, f64)
						}
					} else {
						print.Errorf("分页[%s]第[%d]行第[%d]列必须是浮点数，%s", s.Name, row+1, col+1, err)
						continue rowLoop
					}
				case "bool":
					b := true
					v := strings.ToLower(value)
					switch v {
					case "0", "f", "false", "否":
						b = false
					case "是":
						b = true
					}
					if b {
						data.Append(fieldName, b)
					}
				default:
					if strings.HasPrefix(f.realType, "[]") {
						// 切片
						elementType := strings.TrimPrefix(f.realType, "[]")
						switch elementType {
						case "int", "time.Duration":
							var slice []int
							if value != "" {
								err = json.Unmarshal([]byte(value), &slice)
								if err != nil {
									slice, err = parseValueToIntSlice(value)
									if err != nil {
										print.Errorf("分页[%s]第[%d]行第[%d]列必须是整型数组，%s", s.Name, row+1, col+1, err)
										continue rowLoop
									}
								}
							}
							data.Append(fieldName, slice)
						case "string":
							var slice []string
							if value != "" {
								err = json.Unmarshal([]byte(value), &slice)
								if err != nil {
									slice, err = parseValueToStringSlice(value)
									if err != nil {
										print.Errorf("分页[%s]第[%d]行第[%d]列必须是字符串数组，%s", s.Name, row+1, col+1, err)
										continue
									}
								}
							}
							data.Append(fieldName, slice)
						case "float64":
							var slice []float64
							if value != "" {
								err = json.Unmarshal([]byte(value), &slice)
								if err != nil {
									slice, err = parseValueToFloat64Slice(value)
									if err != nil {
										print.Errorf("分页[%s]第[%d]行第[%d]列必须是浮点数组，%s", s.Name, row+1, col+1, err)
										continue rowLoop
									}
								}
							}
							data.Append(fieldName, slice)
						case "bool":
							var slice []bool
							if value != "" {
								err = json.Unmarshal([]byte(value), &slice)
								if err != nil {
									slice, err = parseValueToBoolSlice(value)
									if err != nil {
										print.Errorf("分页[%s]第[%d]行第[%d]列必须是布尔数组，%s", s.Name, row+1, col+1, err)
										continue
									}
								}
							}
							data.Append(fieldName, slice)
						default:
							if strings.HasPrefix(elementType, "E_") {
								// todo 枚举切片
								e := GetEnumByName(sd.Enums, elementType)
								if e == nil {
									print.Errorf("分页[%s]第[%d]行第[%d]列对应的枚举[%s]不存在", s.Name, row+1, col+1, elementType)
									continue filedLoop
								}
								var slice []int
								if value != "" {
									svs := strings.Split(value, ",")
									for _, sv := range svs {
										v := e.GetValue(sv)
										if v == nil {
											print.Errorf("分页[%s]第[%d]行第[%d]列枚举[%s]的值[%s]不存在", s.Name, row+1, col+1, elementType, sv)
											continue filedLoop
										}
										slice = append(slice, v.Value)
									}
								}
								data.Append(fieldName, slice)
							} else {
								data.Append(fieldName, value)
							}
						}
					} else if strings.HasPrefix(f.realType, "E_") {
						// 枚举
						e := GetEnumByName(sd.Enums, f.realType)
						if e == nil {
							print.Errorf("分页[%s]第[%d]行第[%d]列对应的枚举[%s]不存在", s.Name, row+1, col+1, f.realType)
							continue filedLoop
						}
						v := e.GetValue(value)
						if v == nil {
							print.Errorf("分页[%s]第[%d]行第[%d]列枚举[%s]的值[%s]不存在", s.Name, row+1, col+1, f.realType, value)
							continue filedLoop
						}
						data.Append(fieldName, v.Value)
					} else {
						data.Append(fieldName, value)
					}
				}
			case FieldTypeBaseSlice:
				switch f.realType {
				case "int", "time.Duration":
					var slice []interface{}
					for _, xf := range f.xlsxFields {
						col := xf.col
						cell := sheet.Cell(row, col)
						if cell == nil {
							continue
						}
						// 去除左右空白字符
						value := strings.TrimSpace(cell.Value)
						if value == "" {
							slice = append(slice, 0)
							continue
						}
						i, err := strconv.Atoi(value)
						if err == nil {
							slice = append(slice, i)
						} else {
							// 转成int失败，尝试转成float64
							f64, err := strconv.ParseFloat(value, 64)
							if err == nil {
								slice = append(slice, f64)
							} else {
								print.Errorf("分页[%s]第[%d]行第[%d]列必须是数字，%s", s.Name, row+1, col+1, err)
								continue rowLoop
							}
						}
					}
					if len(slice) > 0 {
						data.Append(fieldName, slice)
					}
				case "string":
					var slice []string
					for _, xf := range f.xlsxFields {
						col := xf.col
						cell := sheet.Cell(row, col)
						if cell == nil {
							continue
						}
						// 去除左右空白字符
						value := strings.TrimSpace(cell.Value)
						if value == "" {
							slice = append(slice, "")
							continue
						}
						slice = append(slice, value)
					}
					if len(slice) > 0 {
						data.Append(fieldName, slice)
					}
				case "bool":
					var slice []bool
					for _, xf := range f.xlsxFields {
						col := xf.col
						cell := sheet.Cell(row, col)
						if cell == nil {
							continue
						}
						// 去除左右空白字符
						value := strings.TrimSpace(cell.Value)
						if value == "" {
							slice = append(slice, false)
							continue
						}
						b := true
						v := strings.ToLower(value)
						switch v {
						case "0", "f", "false":
							b = false
						}
						slice = append(slice, b)
					}
					if len(slice) > 0 {
						data.Append(fieldName, slice)
					}
				case "float64":
					var slice []float64
					for _, xf := range f.xlsxFields {
						col := xf.col
						cell := sheet.Cell(row, col)
						if cell == nil {
							continue
						}
						// 去除左右空白字符
						value := strings.TrimSpace(cell.Value)
						if value == "" {
							slice = append(slice, 0.0)
							continue
						}
						f64, e := strconv.ParseFloat(value, 64)
						if e != nil {
							print.Errorf("分页[%s]第[%d]行第[%d]列必须是浮点数，%s", s.Name, row+1, col+1, err)
							continue rowLoop
						}
						slice = append(slice, f64)
					}
					if len(slice) > 0 {
						data.Append(fieldName, slice)
					}
				default:
					err = fmt.Errorf("分页[%s]不支持的切片元素类型[%s]", s.Name, f.realType)
					return
				}
			case FieldTypeEnum:
				col := f.xlsxFields[0].col
				cell := sheet.Cell(row, col)
				if cell == nil {
					continue
				}
				// 去除左右空白字符
				value := strings.TrimSpace(cell.Value)
				if value == "" || value == "0" || strings.ToLower(value) == "null" {
					data.Append(fieldName, 0)
					continue
				}
				e := GetEnumByName(sd.Enums, f.Type)
				v := e.GetValue(value)
				if v == nil {
					err = fmt.Errorf("分页[%s]字段[%s]未找到[%s]对应的枚举值", s.Name, f.Name, value)
					return
				}
				data.Append(fieldName, v.Value)
			case FieldTypeEnumSlice:
				var slice []interface{}
				for _, xf := range f.xlsxFields {
					col := xf.col
					cell := sheet.Cell(row, col)
					if cell == nil {
						continue
					}
					// 去除左右空白字符
					value := strings.TrimSpace(cell.Value)
					if value == "" || value == "0" || strings.ToLower(value) == "null" {
						slice = append(slice, 0)
						continue
					}
					e := GetEnumByName(sd.Enums, strings.TrimPrefix(f.Type, "[]"))
					v := e.GetValue(value)
					if v == nil {
						err = fmt.Errorf("分页[%s]字段[%s]未找到[%s]对应的枚举值", s.Name, f.Name, value)
						return
					}
					slice = append(slice, v.Value)
				}
				if len(slice) > 0 {
					data.Append(fieldName, slice)
				}
			default:
				err = fmt.Errorf("分页[%s]字段[%s]是不支持的类型[%d]", s.Name, f.Name, f.ft)
				return
			}
		}
		s.Datas = append(s.Datas, data)
	}
	return
}

func (s Sheet) String() string {
	ba, _ := json.Marshal(s)
	return string(ba)
}

type Excel struct {
	Name     string `json:"-"`
	Sheets   []*Sheet
	SheetMap map[string]*Sheet `json:"-"`
}

func NewExcel(name string) *Excel {
	return &Excel{
		Name:     name,
		SheetMap: make(map[string]*Sheet),
	}
}

func (e *Excel) Append(sheet *Sheet) (err error) {
	if sheet.Name == "" {
		return fmt.Errorf("table name is empty")
	}
	if _, ok := e.SheetMap[sheet.Name]; ok {
		return fmt.Errorf("table[%s] already exist", sheet.Name)
	}
	e.Sheets = append(e.Sheets, sheet)
	e.SheetMap[sheet.Name] = sheet
	return
}

type StaticData struct {
	Enums  []*Enum
	Tables []*Sheet
}

func New() *StaticData {
	return &StaticData{}
}

func (sd *StaticData) AppendTable(newTable *Sheet) (err error) {
	if newTable.Name == "" {
		return fmt.Errorf("表名是空的")
	}
	for _, t := range sd.Tables {
		if t.Name == newTable.Name {
			return fmt.Errorf("表名[%s]重复", newTable.Name)
		}
	}
	sd.Tables = append(sd.Tables, newTable)
	return
}
